package com.hrsoft.edi.stub.internal;

import com.taobao.api.internal.mapping.ApiListType;
import com.taobao.api.internal.util.StringUtils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.Map.Entry;

public class XmlWriter
{
    private StringBuffer buf;

    private Stack<Object> calls;

    private boolean omitClassName;

    private String rootTagName;

    private Class<?> stopClass;

    private Set<String> stopProps;

    public XmlWriter(boolean omitClassName, String rootTagName, Class<?> stopClass)
    {
        this.buf = new StringBuffer();
        this.calls = new Stack();
        this.omitClassName = omitClassName;
        this.rootTagName = rootTagName;
        this.stopClass = stopClass;
        this.stopProps = StringUtils.getClassProperties(stopClass, true);
    }

    public XmlWriter()
    {
        this(true, (String)null, (Class)null);
    }

    public String write(Object object)
    {
        this.buf.setLength(0);
        String tagName = this.rootTagName;
        if (tagName == null)
        {
            tagName = StringUtils.toCamelStyle(object.getClass().getSimpleName());
        }

        this.startTag(tagName);
        this.value(object);
        this.endTag(tagName);
        return this.buf.toString();
    }

    private void value(Object object)
    {
        if (object != null && !this.cyclic(object))
        {
            this.calls.push(object);
            if (object instanceof Class)
            {
                this.string(object);
            }
            else if (object instanceof Boolean)
            {
                this.bool((Boolean)object);
            }
            else if (object instanceof Number)
            {
                this.add(object);
            }
            else if (object instanceof String)
            {
                this.string(object);
            }
            else if (object instanceof Character)
            {
                this.string(object);
            }
            else if (object instanceof Map)
            {
                this.map((Map)object);
            }
            else if (object.getClass().isArray())
            {
                this.array(object);
            }
            else if (object instanceof Collection)
            {
//                System.out.println(object.getClass().getAnnotation(ApiListItem.class));
                this.array((Collection)object, null);
            }
            else if (object instanceof Date)
            {
                this.date((Date)object);
            }
            else
            {
                this.bean(object);
            }

            this.calls.pop();
        }
        else
        {
            this.add((Object)null);
        }

    }

    private boolean cyclic(Object object)
    {
        Iterator it = this.calls.iterator();

        Object called;
        do
        {
            if (!it.hasNext())
            {
                return false;
            }

            called = it.next();
        } while (object != called);

        return true;
    }

    private void bean(Object object)
    {
        try
        {
            BeanInfo info = Introspector.getBeanInfo(object.getClass());
            Field[] fields = object.getClass().getDeclaredFields();
            boolean isChildren = this.stopClass != null && this.stopClass.isAssignableFrom(object.getClass());
            PropertyDescriptor[] props = info.getPropertyDescriptors();

            for (int i = 0; i < props.length; ++i)
            {
                PropertyDescriptor prop = props[i];
                String name = prop.getName();

                if (!isChildren || !this.stopProps.contains(name))
                {
                    Field currentField = null;
                    for (Field field : fields)
                    {
                        if (name.equals(field.getName()))
                        {
                            currentField = field;
                        }
                    }
                    if (null != currentField && null != currentField.getAnnotation(ApiListItem.class))
                    {
                        ApiListItem apiListItem = currentField.getAnnotation(ApiListItem.class);

                        if (null != apiListItem)
                        {
                            Method accessor = prop.getReadMethod();
                            if ((!this.omitClassName || !"class".equals(name)) && accessor != null)
                            {
                                if (!accessor.isAccessible())
                                {
                                    accessor.setAccessible(true);
                                }

                                Object value = accessor.invoke(object, (Object[])null);
                                if (value != null && value instanceof Collection)
                                {
                                    this.startTag(apiListItem.rootName());
                                    this.array((Collection)value, apiListItem.value());
                                    this.endTag(apiListItem.rootName());
                                }
                            }
                        }
                    }
                    else
                    {
                        Method accessor = prop.getReadMethod();
                        if ((!this.omitClassName || !"class".equals(name)) && accessor != null)
                        {
                            if (!accessor.isAccessible())
                            {
                                accessor.setAccessible(true);
                            }

                            Object value = accessor.invoke(object, (Object[])null);
                            if (value != null)
                            {
                                this.add(name, value);
                            }
                        }
                    }
                }
            }

            Field[] ff = object.getClass().getFields();

            for (int i = 0; i < ff.length; ++i)
            {
                Field field = ff[i];
                Object value = field.get(object);
                if (value != null)
                {
                    this.add(field.getName(), value);
                }
            }

        }
        catch (Exception var10)
        {
            throw new RuntimeException(var10);
        }
    }

    private void add(String name, Object value)
    {
        this.startTag(name);
        this.value(value);
        this.endTag(name);
    }

    private void map(Map<?, ?> map)
    {
        Iterator it = map.entrySet().iterator();

        while (it.hasNext())
        {
            Entry<?, ?> e = (Entry)it.next();
            this.startTag(String.valueOf(e.getKey()));
            this.value(e.getValue());
            this.endTag(String.valueOf(e.getKey()));
        }

    }

    /***
     *
     *  tagName 不为null时直接循坏赋值，为null时走原逻辑
     * @param object
     * @param tagName
     */
    private void array(Collection<?> object, String tagName)
    {
        Iterator i$ = object.iterator();
        if (tagName != null)
        {
            while (i$.hasNext())
            {
                Object item = i$.next();
                this.startTag(tagName);
                this.value(item);
                this.endTag(tagName);

            }
        }
        else
        {
            while (i$.hasNext())
            {
                Object item = i$.next();

                ApiListType apiListType = (ApiListType)item.getClass().getAnnotation(ApiListType.class);
                if (apiListType != null)
                {
                    tagName = apiListType.value();
                }
                else
                {

                    tagName = StringUtils.toCamelStyle(item.getClass().getSimpleName());

                }

                this.startTag(tagName);
                this.value(item);
                this.endTag(tagName);

            }
        }

    }

    private void array(Object object)
    {
        String tagName = null;
        int length = Array.getLength(object);

        for (int i = 0; i < length; ++i)
        {
            Object item = Array.get(object, i);
            if (tagName == null)
            {
                tagName = StringUtils.toCamelStyle(item.getClass().getSimpleName());
            }

            this.startTag(tagName);
            this.value(item);
            this.endTag(tagName);
        }

    }

    private void bool(boolean b)
    {
        this.add(b ? "true" : "false");
    }

    private void date(Date date)
    {
        this.add(StringUtils.formatDateTime(date));
    }

    private void string(Object obj)
    {
        this.add(StringUtils.escapeXml(obj.toString()));
    }

    private void add(Object obj)
    {
        this.buf.append(obj);
    }

    private void startTag(String tagName)
    {
        this.buf.append("<");
        this.buf.append(tagName);
        this.buf.append(">");
    }

    private void endTag(String tagName)
    {
        this.buf.append("</");
        this.buf.append(tagName);
        this.buf.append(">");
    }
}

